﻿using System;
using System.Linq;
using System.Net;
using System.Text;
using Newtonsoft.Json;

namespace QQ2564874169.Nubbo
{
    public interface INubboServer
    {
        event EventHandler<ServiceRequest> NewRequest;
        void Start();
        void Close();
    }

    public class NubboHttpServer: INubboServer
    {
        public event EventHandler<ServiceRequest> NewRequest;
        
        private HttpListener _listener;

        public NubboHttpServer(int port, bool useHttps = false)
        {
            _listener = new HttpListener();

            if (useHttps)
            {
                _listener.Prefixes.Add("https://*:" + port + "/");
            }
            else
            {
                _listener.Prefixes.Add("http://*:" + port + "/");
            }
        }

        protected virtual void OnNewRequest(ServiceRequest request)
        {
            NewRequest?.Invoke(this, request);
        }

        private static void OnResponse(ServiceResult result, object state)
        {
            var http = (HttpListenerContext)state;

            foreach (var key in result.Header.Keys)
            {
                http.Response.Headers.Add(key, result.Header[key]);
            }

            if (result.Error != null)
            {
                var data = Encoding.UTF8.GetBytes(result.Error);
                http.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                http.Response.ContentLength64 = data.Length;
                http.Response.OutputStream.Write(data, 0, data.Length);
            }
            else if (result.Data != null)
            {
                var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(result.Data));
                http.Response.StatusCode = (int)HttpStatusCode.OK;
                http.Response.ContentLength64 = data.Length;
                http.Response.OutputStream.Write(data, 0, data.Length);
            }

            http.Response.Close();
        }

        private static ServiceRequest GetServiceRequest(HttpListenerRequest httpRequest)
        {
            var request = new ServiceRequest(httpRequest.RawUrl, OnResponse, httpRequest);

            foreach (var key in httpRequest.Headers.AllKeys)
            {
                var value = httpRequest.Headers.GetValues(key);
                if (value != null)
                    request.Param.Header.Add(key, value.Last());
            }
            if (httpRequest.HasEntityBody)
            {
                var data = new byte[httpRequest.ContentLength64];
                httpRequest.InputStream.Read(data, 0, data.Length);
                var str = (httpRequest.ContentEncoding ?? Encoding.UTF8).GetString(data);
                var items = str.Split(new[] {'&'}, StringSplitOptions.RemoveEmptyEntries);
                foreach (var item in items)
                {
                    var kv = item.Split('=');
                    if (kv.Length == 1)
                    {
                        request.Param.Param.Add(item, "");
                    }
                    else if (kv.Length == 2)
                    {
                        request.Param.Param.Add(kv[0], kv[1]);
                    }
                    else
                    {
                        request.Param.Param.Add(kv[0], string.Join("=", kv.Skip(1)));
                    }
                }
            }
            return request;
        }

        private void BeginGetContext(IAsyncResult ar)
        {
            var httpContext = _listener.EndGetContext(ar);
            _listener.BeginGetContext(BeginGetContext, null);

            var request = GetServiceRequest(httpContext.Request);

            OnNewRequest(request);
        }

        public void Start()
        {
            _listener.BeginGetContext(BeginGetContext, null);
            _listener.Start();
        }

        public void Close()
        {
            _listener.Stop();
        }
    }
}
